/*
  Ce fichier contient les éléments nécessaires à la structure d'arbre utilisée lors de l'aide à la résolution 
  de l' attaque par fréquence.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "header.h"




void copy_id(Tree original, Tree* ptr_copy)
{
  int i;
  for (i = 0; i < MAX_NUMBER_OF_CHILDREN; ++i)
    {
      ptr_copy->id[i] = original.id[i];
    }
}

void update_id(Tree* ptr_tree, int c, int d)
{
  ptr_tree->id[d] = c;
}


int number_next_child(Tree tree)
{
  int num = 0;
  if(tree.nb_children >= tree.nb_children_max)
    {
      fprintf(stderr, "Aucun fils disponible \n");
      num = -1;
    }
  else
    {
      num = (tree.nb_children);
    }
  return num;
}


void copy_path(Tree original, Tree* ptr_copy)
{
  for (int i = 0; i < MAX_NUMBER_OF_CHILDREN; ++i)
    {
      ptr_copy->path[i] = original.path[i];
    }
}


void update_path(Tree parent, Tree* ptr_child)
{
  int num = number_next_child(parent); //B sera le num-ieme fils de A
  int i = 0;
  while((ptr_child->path[i] <= MAX_NUMBER_OF_CHILDREN) && (ptr_child->path[i] >=0) && (i < MAX_NUMBER_OF_CHILDREN))
    {
      i++;
    }
  if(i < MAX_NUMBER_OF_CHILDREN)
    {
      ptr_child->path[i] = num;
    }
  else
    {
      fprintf(stderr, "Une erreur est apparue lors de la mise a jour du chemin \n");
      exit(1);
    }
}



void update_children(Tree* ptr_parent , Tree* ptr_child)
{
  int i = number_next_child(*ptr_parent);
  if(i != -1)
    {
      (ptr_parent->children[i]) = ptr_child;
    }
  else
    {
      fprintf(stderr, "Erreur : plus de place dans l'arbre recevant\n");
    }
}

Tree* go_to_child(Tree tree, int i)
{
  return tree.children[i];
}




Tree* go_to_parent(Tree* ptr_tree)
{
  if (is_root(*ptr_tree))
    {
      printf("Vous êtes déjà à la racine, impossible d'atteindre le père\n");
      return ptr_tree;
    }
  else
    {
      return (ptr_tree->parent);
    }
}


Tree* go_to_root(Tree* ptr_tree)
{
  if(!(is_root(*ptr_tree)))
    {
      while(!(is_root(*ptr_tree->parent)))
	{
	  ptr_tree = ptr_tree->parent;
	}
    }
  return ptr_tree;
}


Tree* add_child(Tree* ptr_parent, int p, int c)
{
  if ((ptr_parent->nb_children <= ptr_parent->nb_children_max))
    {
      Tree* ptr_child = (Tree*)malloc(sizeof(Tree));
      ptr_child->id = (int*)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(int));
      ptr_child->path = (int*)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(int));
      ptr_child->parent = (Tree*)malloc(sizeof(struct Tree));
      ptr_child->children = (Tree**)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(Tree*));
      copy_id(*ptr_parent, ptr_child);
      update_id(ptr_child, p, c);
      copy_path(*ptr_parent, ptr_child);
      update_path(*ptr_parent,ptr_child);
      ptr_child->parent = ptr_parent;
      update_children(ptr_parent,ptr_child);
      (ptr_parent->nb_children)++;
      ptr_child->nb_children_max = (ptr_parent->nb_children_max) -1;
      ptr_child->nb_children = 0;
      return ptr_child;
    }
  else
    {
      fprintf(stderr, "Plus de place pour ajouter un fils ici ! \n");
      return ptr_parent;
    }
}


Tree* create()
{
  Tree* ptr_root = (Tree*) malloc(sizeof(Tree));
  ptr_root->id = (int*)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(int));
  ptr_root->path = (int*)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(int));
  for (int i = 0; i < MAX_NUMBER_OF_CHILDREN; ++i)
    {
      ptr_root->id[i] = -1;
      ptr_root->path[i] = -1;
    }
  ptr_root->nb_children = 0;
  ptr_root->nb_children_max = MAX_NUMBER_OF_CHILDREN;
  ptr_root->parent = (Tree*)malloc(sizeof(Tree));
  ptr_root->parent = NULL;
  ptr_root->children = (Tree**)malloc(MAX_NUMBER_OF_CHILDREN * sizeof(Tree*));
  return ptr_root;
}

void print_path(Tree tree)
{
  int i = 1;
  printf("[%d", tree.path[0]);
  while(i < MAX_NUMBER_OF_CHILDREN && tree.path[i] >= 0 && tree.path[i] <= MAX_NUMBER_OF_CHILDREN)
    {
      printf(".%d", tree.path[i]);
      ++i;
    }
  printf("]\n");
}

void print_id(Tree tree)
{
  for(int i = 0; i < MAX_NUMBER_OF_CHILDREN; ++i)
    {
      if(tree.id[i] != -1)
	{
	  printf("%c : %c   ", int_to_char(i, 1), int_to_char(tree.id[i], 0));  // ex : id[0] = 2 <-> a : C //
	}
    }
  printf("\n");
}


int is_root(Tree tree)
{
  return (tree.nb_children_max == MAX_NUMBER_OF_CHILDREN);
}


void remove_tree(Tree *ptr_tree)
{
  int n = ptr_tree->nb_children;
  for(int position = 0; position < n; ++position)
    {
      remove_tree(ptr_tree->children[position]);
      //free(ptr_tree->children[position]);
    }
  free(ptr_tree->path);
  free(ptr_tree->id);
  free(ptr_tree);
}

/*
  Fonction permettant l'affichage du texte en cours de décryptage. En fonction du tableau d'identification de l'arbre courant,
  cette fonction affiche le texte de tel façon : 
  - Les lettres encore non décryptées restent en majuscules
  - Les lettres décryptées le sont, et apparaissent en minuscules
  Pour cela la fonction regarde en fonction du tableau id du noeud courrant quelles sont les lettres déjà découvertes. Il les flag à 
  la valeur 1 (0 pour les lettres non encore découvertes).
  Puis elle appelle une fonction int_to_string particulière qui exploite les flags pour afficher les lettres en minuscules ou en majuscules.

  L'argument "out" indique quelle sortie doit être utiliser. 
*/
void print_tree(Tree tree, int* txt, int size_txt, FILE* out)
{
  int current[size_txt];
  for(int i = 0; i < size_txt; ++i)
    {
      current[i] = txt[i];
    }
  for (int j = 0; j < size_txt; ++j)
    {
      if ((j != 0)&&(j%4 == 0))
	{
	  fprintf(out," "); //Affiche un espace tous les 4caractères
	}
      if(is_identified(current[j], tree.id) == 0)
	{
	  fprintf(out,"%c",int_to_char(current[j], 0)); //0 <=> en maj
	}
      else
	{
	  //on cherche i tel que tree.id[i] == current[j]
	  for(int i = 0; i < MAX_NUMBER_OF_CHILDREN; ++i)
	    {
	      if (tree.id[i] == current[j])
		{
		  fprintf(out,"%c", int_to_char(i,1)); //1 <=> en minuscule
		}
	    }
	}
    }
  fprintf(out,"\n");
}


/*
  Fonction permettant de trouver la prochaine identification.

  @param ptr_tree Arbre sur lequel on veut faire l'identification. 
  @param p indice de la lettre cryptee que l'on souhaite identifiee. p a ete trouve grâce à la fonction prochaineProposion

  @return Un tableau contenant deux indices correspondant au décryptage de la lettre 'p' donnée en paramètre. Le 1er est la correspondance la plus probable
  et le 2nd est la 2e plus probable.
*/
int* identify(Tree* ptr_tree, int p)
{
  /*
    min1 et min2 sont les deux distances minimales (avec min1 <= min2)
    imin1 et imin2 sont les indices des distances minimales.
  */
  int imax1 = -1;
  int imax2 = -1;
  float max1 = -1; 
  float max2 = -1;  
  float d;

  for(int c = 0; c <  MAX_NUMBER_OF_CHILDREN; ++c)
    {
      /*
	On choisi la lettre la plus courante dans la langue française qui n'a pas encore été utilisé
-> c tel que 
 freqT[c] soit max
 id[c] == -1
 c est libre dans les fils de ptr_tree
       */
      int is_valid_children = ((ptr_tree->id[c] == -1) && !(exist_in_children(*ptr_tree, p, c))); 
      if(is_valid_children) //c n'est utilisé ni dans ptr_tree ni dans aucun fils de ptr_tree comme correspondance à p
	{
	  d = th_frequency(c);
	  if(d > max1)
	    {
	      max2 = max1;
	      imax2 = imax1;
	      max1 = d;
	      imax1 = c;
	    }
	  else
	    {
	      if(d > max2)
		{
		  max2 = d;
		  imax2 = c;
		}
	    }
	}
    }
  int* res = (int*) malloc(2*sizeof(int));
  res[0] = imax1; //c'est la correspondance la plus probable
  res[1] = imax2; //c'est la correspondance de secours
  return res;
}


int exist_in_children(Tree tree, int prop, int cor)
{
  int exist = 0;
  int j = 0;
  while((j < tree.nb_children) && !exist)
    {
      exist = (tree.children[j]->id[cor] == prop); //Le couple (prop, cor) existe dans un des fils
      ++j;
    }
return exist;
}



/*
  Retourne les deux meilleurs bigrammes possibles (disponibles et maximaux)
 */
void next_bigrams(Tree* ptr_tree, int p,int* bigrams)
{
  /*
    min1 et min2 sont les deux distances minimales (avec min1 <= min2)
    imin1 et imin2 sont les indices des distances minimales.
  */
  int imax1 = -1;
  int imax2 = -1;
  float max1 = -1; 
  float max2 = -1; 
  float d;

  for(int c = 0; c <  MAX_NUMBER_OF_CHILDREN; ++c)
    {
      /*
	On choisi la lettre du bigramme le plus courant dans la langue française qui n'a pas encore été utilisé
-> c tel que 
 freqT_b[c] soit max
 id[c] == -1
 c est libre dans les fils de ptr_tree
       */
      int is_valid_children = ((ptr_tree->id[c] == -1) && !(exist_in_children(*ptr_tree, p, c))); 
      if(is_valid_children) //c n'est utilisé ni dans ptr_tree ni dans aucun fils de ptr_tree comme correspondance à p
	{
	  d = th_frequency_b(c);
	  if(d > max1)
	    {
	      max2 = max1;
	      imax2 = imax1;
	      max1 = d;
	      imax1 = c;
	    }
	  else
	    {
	      if(d > max2)
		{
		  max2 = d;
		  imax2 = c;
		}
	    }
	}
    }
  bigrams[0] = imax1; //c'est la correspondance la plus probable
  bigrams[1] = imax2; //c'est la correspondance de secours
}
